#include "./log_uart.h"
#include "../device/device.h"
#include <stdio.h>
#include <stdarg.h>
#include "../utils/alg.h"
#include "../utils/codec.h"
#include "../utils/proto.h"

namespace driver {

utils::Object<LogUart_t> logUart;

// PB2 <=> RXD1
// PB3 <=> TXD1
LogUart_t::LogUart_t()
{

    GPIO_InitStruType gpio;
    UART_InitStruType uart;

    gpio.GPIO_Signal = GPIO_Pin_Signal_Digital;
    gpio.GPIO_Func = GPIO_Func_2;
    gpio.GPIO_Direction = GPIO_Dir_Out;
    gpio.GPIO_PUEN = GPIO_PUE_Input_Disable;
    gpio.GPIO_PDEN = GPIO_PDE_Input_Disable;
    gpio.GPIO_OD = GPIO_ODE_Output_Disable;
    gpio.GPIO_DS = GPIO_DS_Output_Strong;
    GPIO_Init(GPIOB, GPIO_Pin_3, &gpio);

    gpio.GPIO_Direction = GPIO_Dir_In;
    gpio.GPIO_PUEN = GPIO_PUE_Input_Enable;
    GPIO_Init(GPIOB, GPIO_Pin_2, &gpio);

    uart.UART_StopBits = UART_StopBits_1;
    uart.UART_TxMode = UART_DataMode_8;
    uart.UART_TxPolar = UART_Polar_Normal;
    uart.UART_RxMode = UART_DataMode_8;
    uart.UART_RxPolar = UART_Polar_Normal;
    uart.UART_BaudRate = 9600;
    uart.UART_ClockSet = UART_Clock_1;
    UART_Init(UART1, &uart);    

    write_unlock();

    UART_TBIMConfig(UART1, UART_TRBIM_Byte);
    UART_RBIMConfig(UART1, UART_TRBIM_Byte);
    UART_ITConfig(UART1, UART_IT_RB, ENABLE);
    UART_ITConfig(UART1, UART_IT_TXIDLE, ENABLE);
    NVIC_EnableIRQ(UART1_IRQn);
    UART1_TxEnable();
    UART1_RxEnable();
}

bool LogUart_t::next_uint16(uint16_t &val) {
    uint8_t h, l;
    if (!next_byte(h)) {
        return false;
    }
    if (!next_byte(l)) {
        return false;
    }
    val = (h << 8) + l;
    return true;
}

utils::Error_t LogUart_t::recv_proto(utils::ByteView_t &bw) {
    using namespace utils;

    bool flag = false;
    uint8_t val;
    for (;;) {
        if (!next_byte(val)) {
            return utils::ec::RecvTimeout;
        }
        if (flag && (val == proto::Head1)) {
            break;
        }
        flag = val == proto::Head0;
    }

    uint16_t len;
    if (!next_uint16(len)) {
        return utils::ec::RecvTimeout;
    }
    if (len < 8) {
        return utils::ec::LenFail;
    }
    len -= 4;
    for (uint32_t i = 0; i < len; i ++) {
        if (!next_byte(val)) {
            return utils::ec::RecvTimeout;
        }
        bw.buf[i] = val;
    }
    uint8_t s = utils::alg::xor_sum(bw.buf + 2, len - 4);
    if (s != bw.buf[len - 2]) {
        return utils::ec::SumFail;
    }
    if (proto::End != bw.buf[len - 1]) {
        return utils::ec::EndFail;
    }
    bw.len = len - 2;
    return utils::ec::Ok;
}

void LogUart_t::recv_byte(uint8_t byte) {
    m_mq.put(byte);
}

void LogUart_t::printf(const char *format, ...) {
    write_lock();

    using namespace utils;

    encode_uint16(m_tx_buf, proto::Head);
    encode_uint16(m_tx_buf + 4, proto::Log + proto::SalveFlag);

    va_list ap;
    va_start(ap, format);
    int n = vsprintf(reinterpret_cast<char *>(m_tx_buf + 6), format, ap);
    
    encode_uint16(m_tx_buf + 2, n + 8);
    uint8_t s = alg::xor_sum(m_tx_buf + 6, n);
    encode_uint8(m_tx_buf + 6 + n, s);
    encode_uint8(m_tx_buf + 7 + n, proto::End);

    write_impl(m_tx_buf, n + 8);

    write_unlock();
}

void LogUart_t::write_ack() {
    using namespace utils;

    write_lock();

    encode_uint16(m_tx_buf, proto::Head);
    encode_uint16(m_tx_buf + 2, 8);
    encode_uint16(m_tx_buf + 4, proto::Ack | proto::SalveFlag);
    encode_uint8(m_tx_buf + 6, 0);
    encode_uint8(m_tx_buf + 7, proto::End);

    write_impl(m_tx_buf, 8);

    write_unlock();
}

void LogUart_t::write_uint8(uint16_t req, uint8_t val) {
    using namespace utils;

    write_lock();

    encode_uint16(m_tx_buf, proto::Head);
    encode_uint16(m_tx_buf + 2, 9);
    encode_uint16(m_tx_buf + 4, req | proto::SalveFlag);
    encode_uint8(m_tx_buf + 6, val);
    encode_uint8(m_tx_buf + 7, val);
    encode_uint8(m_tx_buf + 8, proto::End);

    write_impl(m_tx_buf, 9);

    write_unlock();
}

void LogUart_t::write(const uint8_t *buf, uint32_t size) {
    write_lock();
    write_impl(buf, size);
    write_unlock();
}

void LogUart_t::write_impl(const uint8_t *buf, uint32_t size) {
    tx_clear();
    for (uint32_t i = 0; i < size; i ++) {
        uint8_t c = buf[i];
       // while(UART_GetFlagStatus(UART1, UART_FLAG_TB) == RESET);
        UART_SendByte(UART1, c);
        tx_wait();
    }
}

}

using namespace driver;

extern "C"
void UART1_IRQHandler() {

    if (UART_GetFlagStatus(UART1, UART_FLAG_RB) != RESET) {
        uint8_t c = UART_RecByte(UART1);
        logUart->recv_byte(c);
        return;
    }

    if (UART_GetFlagStatus(UART1, UART_FLAG_TXIDLE) != RESET) {
        UART_ClearITPendingBit(UART1, UART_Clr_TXIDLE);
        logUart->tx_signal();
    }
}


